"""
jlpay日志配置工具模块

提供统一的日志配置工具，用于配置jlpay库中所有模块的日志级别，
包括http、service等模块，让用户能够方便地控制日志输出的详细程度。
"""

import logging
import sys
import os
import time
from logging.handlers import RotatingFileHandler
from typing import Dict, List, Optional, Union, Any

# =================== 常量定义 =====================
# 日志级别映射
LOG_LEVELS = {
    "DEBUG": logging.DEBUG,
    "INFO": logging.INFO,
    "WARNING": logging.WARNING,
    "ERROR": logging.ERROR,
    "CRITICAL": logging.CRITICAL
}

# 默认日志格式
DEFAULT_LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
DEFAULT_DETAILED_LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(funcName)s() - %(message)s'

# 默认滚动文件设置
DEFAULT_MAX_BYTES = 10 * 1024 * 1024  # 10MB
DEFAULT_BACKUP_COUNT = 10
DEFAULT_LOG_NAME_PREFIX = "jlpay"

# 常见子模块列表
COMMON_MODULES = ['http', 'service', 'base', 'core', 'model', 'services', 'utils']

# 环境变量前缀
ENV_PREFIX = "JLPAY_LOG_"

# =================== 辅助函数 =====================
def normalize_log_level(level: Union[str, int]) -> int:
    """
    标准化日志级别，将字符串转换为对应的日志级别整数值
    
    Args:
        level: 日志级别，可以是字符串或整数
        
    Returns:
        整数形式的日志级别
    """
    if isinstance(level, str):
        return LOG_LEVELS.get(level.upper(), logging.INFO)
    return level


def create_formatter(format_str: Optional[str] = None) -> logging.Formatter:
    """
    创建日志格式化器
    
    Args:
        format_str: 格式字符串，如果为None则使用默认格式
        
    Returns:
        配置好的格式化器
    """
    if format_str is None:
        format_str = DEFAULT_LOG_FORMAT
    return logging.Formatter(format_str)


def add_console_handler(logger: logging.Logger, level: int, 
                        formatter: logging.Formatter) -> None:
    """
    为日志记录器添加控制台处理器
    
    Args:
        logger: 要添加处理器的日志记录器
        level: 处理器的日志级别
        formatter: 处理器使用的格式化器
    """
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)
    console_handler.setLevel(level)
    logger.addHandler(console_handler)


def add_file_handler(logger: logging.Logger, log_file: str, level: int,
                     formatter: logging.Formatter) -> None:
    """
    为日志记录器添加文件处理器
    
    Args:
        logger: 要添加处理器的日志记录器
        log_file: 日志文件路径
        level: 处理器的日志级别
        formatter: 处理器使用的格式化器
    """
    # 确保日志文件目录存在
    log_dir = os.path.dirname(log_file)
    if log_dir and not os.path.exists(log_dir):
        os.makedirs(log_dir, exist_ok=True)
    
    file_handler = logging.FileHandler(log_file, encoding='utf-8')
    file_handler.setFormatter(formatter)
    file_handler.setLevel(level)
    logger.addHandler(file_handler)


def add_rotating_file_handler(logger: logging.Logger, log_file: str, level: int,
                              formatter: logging.Formatter, max_bytes: int = DEFAULT_MAX_BYTES,
                              backup_count: int = DEFAULT_BACKUP_COUNT) -> None:
    """
    为日志记录器添加滚动文件处理器
    
    Args:
        logger: 要添加处理器的日志记录器
        log_file: 日志文件路径
        level: 处理器的日志级别
        formatter: 处理器使用的格式化器
        max_bytes: 单个日志文件最大大小（字节）
        backup_count: 最大备份文件数
    """
    # 确保日志文件目录存在
    log_dir = os.path.dirname(log_file)
    if log_dir and not os.path.exists(log_dir):
        os.makedirs(log_dir, exist_ok=True)
    
    file_handler = RotatingFileHandler(
        filename=log_file,
        maxBytes=max_bytes,
        backupCount=backup_count,
        encoding='utf-8'
    )
    file_handler.setFormatter(formatter)
    file_handler.setLevel(level)
    logger.addHandler(file_handler)


def configure_module_loggers(parent_logger: logging.Logger, level: int, 
                             module_levels: Optional[Dict[str, Union[str, int]]] = None, 
                             propagate: bool = False) -> None:
    """
    配置模块级别的日志记录器
    
    Args:
        parent_logger: 父日志记录器
        level: 默认日志级别
        module_levels: 模块特定的日志级别字典
        propagate: 是否向上传播日志
    """
    # 配置常见子模块日志记录器
    for module in COMMON_MODULES:
        module_name = f"jlpay.{module}"
        module_logger = logging.getLogger(module_name)
        
        # 检查是否有特定的模块级别设置
        if module_levels and module_name in module_levels:
            module_level = normalize_log_level(module_levels[module_name])
        else:
            module_level = level
        
        module_logger.setLevel(module_level)
        module_logger.propagate = propagate
    
    # 如果提供了模块特定的级别设置，应用这些设置
    if module_levels:
        for module_name, module_level in module_levels.items():
            # 跳过已经配置过的常见模块
            if any(module_name == f"jlpay.{m}" for m in COMMON_MODULES):
                continue
                
            module_level = normalize_log_level(module_level)
            module_logger = logging.getLogger(module_name)
            module_logger.setLevel(module_level)
            module_logger.propagate = propagate


def log_configuration_result(logger: logging.Logger, level: int, 
                            module_levels: Optional[Dict[str, Union[str, int]]] = None,
                            log_file: Optional[str] = None) -> None:
    """
    记录配置完成的日志信息
    
    Args:
        logger: 日志记录器
        level: 配置的日志级别
        module_levels: 模块特定的日志级别字典
        log_file: 日志文件路径，如果有的话
    """
    # 记录配置完成的日志
    if level <= logging.DEBUG:
        logger.debug("日志配置初始化完成，根级别: %s", logging.getLevelName(level))
        if log_file:
            logger.debug("日志文件路径: %s", log_file)
            
        if module_levels:
            for module_name, module_level in module_levels.items():
                level_name = logging.getLevelName(normalize_log_level(module_level))
                logger.debug("模块 %s 日志级别: %s", module_name, level_name)
    else:
        if log_file:
            logger.info("日志配置初始化完成，日志文件: %s", log_file)
        else:
            logger.info("日志配置初始化完成")


def get_or_create_logger(logger_name: Optional[str] = None) -> logging.Logger:
    """
    获取或创建日志记录器
    
    Args:
        logger_name: 日志记录器名称，如果为None则返回根日志记录器
        
    Returns:
        日志记录器实例
    """
    if logger_name:
        return logging.getLogger(logger_name)
    return logging.getLogger()


def prepare_logger(logger: logging.Logger, level: int, clear_handlers: bool = True) -> None:
    """
    准备日志记录器，设置级别并可选择清除现有处理器
    
    Args:
        logger: 要准备的日志记录器
        level: 日志级别
        clear_handlers: 是否清除现有处理器
    """
    logger.setLevel(level)
    
    if clear_handlers and logger.handlers:
        logger.handlers.clear()


# =================== 公共函数 =====================
def configure_jlpay_logging(level: Union[str, int] = logging.INFO, 
                           log_file: Optional[str] = None, 
                           format_str: Optional[str] = None, 
                           module_levels: Optional[Dict[str, Union[str, int]]] = None, 
                           propagate: bool = False) -> logging.Logger:
    """
    配置jlpay所有模块的日志级别
    
    Args:
        level: 全局日志级别，默认为INFO，可以是整数值或字符串('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
        log_file: 日志文件路径，默认为None（仅控制台输出）
        format_str: 日志格式字符串，默认为None（使用标准格式）
        module_levels: 模块特定日志级别的字典，如 {'jlpay.http': logging.DEBUG}
        propagate: 是否向上传播日志，默认为False
    
    Returns:
        根日志记录器实例
    
    Examples:
        >>> from jlpay.utils.log_config import configure_jlpay_logging
        >>> import logging
        >>> # 最简单的用法，将日志级别设置为INFO
        >>> configure_jlpay_logging()
        >>> # 设置为DEBUG级别，显示更详细的日志
        >>> configure_jlpay_logging(level=logging.DEBUG)
        >>> # 设置为WARNING级别，只显示警告和错误
        >>> configure_jlpay_logging(level="WARNING")
        >>> # 同时输出到文件
        >>> configure_jlpay_logging(log_file="jlpay.log")
        >>> # 为不同模块设置不同的日志级别
        >>> configure_jlpay_logging(module_levels={'jlpay.http': logging.DEBUG, 'jlpay.service': logging.INFO})
    """
    # 标准化日志级别
    level = normalize_log_level(level)
    
    # 配置根日志记录器
    jlpay_logger = logging.getLogger("jlpay")
    prepare_logger(jlpay_logger, level)
    
    # 创建格式化器
    formatter = create_formatter(format_str)
    
    # 添加控制台处理器
    add_console_handler(jlpay_logger, level, formatter)
    
    # 如果指定了日志文件，添加文件处理器
    if log_file:
        add_file_handler(jlpay_logger, log_file, level, formatter)
    
    # 设置是否向上传播
    jlpay_logger.propagate = propagate
    
    # 配置模块级别的日志记录器
    configure_module_loggers(jlpay_logger, level, module_levels, propagate)
    
    # 记录配置完成的日志
    log_configuration_result(jlpay_logger, level, module_levels, log_file)
    
    return jlpay_logger


def get_log_level_from_env(env_var: str = "JLPAY_LOG_LEVEL", 
                          default: int = logging.INFO) -> int:
    """
    从环境变量获取日志级别
    
    Args:
        env_var: 环境变量名称，默认为"JLPAY_LOG_LEVEL"
        default: 默认日志级别，当环境变量不存在或无效时使用
    
    Returns:
        日志级别（logging级别常量）
    """
    level_name = os.environ.get(env_var, "").upper()
    if level_name and level_name in LOG_LEVELS:
        return LOG_LEVELS[level_name]
    return default


def configure_jlpay_logging_from_env() -> logging.Logger:
    """
    根据环境变量配置jlpay日志
    
    环境变量:
        JLPAY_LOG_LEVEL: 日志级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL)
        JLPAY_LOG_FILE: 日志文件路径
        JLPAY_LOG_FORMAT: 日志格式字符串
    
    Returns:
        根日志记录器实例
    """
    level = get_log_level_from_env(f"{ENV_PREFIX}LEVEL", logging.INFO)
    log_file = os.environ.get(f"{ENV_PREFIX}FILE")
    format_str = os.environ.get(f"{ENV_PREFIX}FORMAT")
    
    # 解析模块特定的日志级别 (例如 JLPAY_LOG_HTTP=DEBUG)
    module_levels = {}
    for env_var, value in os.environ.items():
        if (env_var.startswith(ENV_PREFIX) and 
            env_var not in (f"{ENV_PREFIX}LEVEL", f"{ENV_PREFIX}FILE", f"{ENV_PREFIX}FORMAT")):
            module_name = env_var[len(ENV_PREFIX):].lower()  # 移除 "JLPAY_LOG_" 前缀
            if module_name:
                module_level = value.upper()
                if module_level in LOG_LEVELS:
                    module_levels[f"jlpay.{module_name}"] = LOG_LEVELS[module_level]
    
    return configure_jlpay_logging(
        level=level,
        log_file=log_file,
        format_str=format_str,
        module_levels=module_levels
    )


def configure_rotating_file_logging(level: Union[str, int] = logging.INFO, 
                                   log_dir: Optional[str] = None, 
                                   log_name_prefix: str = DEFAULT_LOG_NAME_PREFIX,
                                   log_format: Optional[str] = None,
                                   max_bytes: int = DEFAULT_MAX_BYTES,
                                   backup_count: int = DEFAULT_BACKUP_COUNT,
                                   module_levels: Optional[Dict[str, Union[str, int]]] = None,
                                   propagate: bool = True,
                                   logger_name: Optional[str] = None) -> logging.Logger:
    """
    配置带有滚动文件的日志系统
    
    Args:
        level: 全局日志级别，默认为INFO
        log_dir: 日志文件目录，默认为None（使用当前目录下的logs目录）
        log_name_prefix: 日志文件名前缀，默认为"jlpay"
        log_format: 日志格式字符串，默认为None（使用详细格式）
        max_bytes: 单个日志文件最大大小，默认为10MB
        backup_count: 最大备份文件数，默认为10
        module_levels: 模块特定日志级别的字典
        propagate: 是否向上传播日志，默认为True
        logger_name: 要配置的日志记录器名称，默认为None（配置根日志记录器）
    
    Returns:
        配置好的日志记录器实例
    
    Examples:
        >>> from jlpay.utils.log_config import configure_rotating_file_logging
        >>> import logging
        >>> # 基本用法
        >>> logger = configure_rotating_file_logging()
        >>> # 自定义设置
        >>> logger = configure_rotating_file_logging(
        ...     level=logging.DEBUG,
        ...     log_dir="/var/log/myapp",
        ...     log_name_prefix="myapp",
        ...     max_bytes=5*1024*1024,  # 5MB
        ...     backup_count=5
        ... )
    """
    # 标准化日志级别
    level = normalize_log_level(level)
    
    # 获取或创建日志记录器
    logger = get_or_create_logger(logger_name)
    prepare_logger(logger, level)
    
    # 创建格式化器（使用详细格式）
    if log_format is None:
        log_format = DEFAULT_DETAILED_LOG_FORMAT
    formatter = create_formatter(log_format)
    
    # 添加控制台处理器
    add_console_handler(logger, level, formatter)
    
    # 创建日志目录（如果不存在）
    if log_dir is None:
        log_dir = os.path.join(os.getcwd(), "logs")
    
    if not os.path.exists(log_dir):
        os.makedirs(log_dir, exist_ok=True)
    
    # 设置文件命名规则：prefix-yyMMdd.log
    current_date = time.strftime("%y%m%d")
    log_file_path = os.path.join(log_dir, f"{log_name_prefix}-{current_date}.log")
    
    # 添加滚动文件处理器
    add_rotating_file_handler(
        logger, 
        log_file_path, 
        level, 
        formatter, 
        max_bytes, 
        backup_count
    )
    
    # 设置是否向上传播
    logger.propagate = propagate
    
    # 配置模块级别的日志记录器
    if module_levels:
        configure_module_loggers(logger, level, module_levels, propagate)
    
    # 记录配置完成的日志
    log_configuration_result(logger, level, module_levels, log_file_path)
    
    return logger 